package com.googlecode.groovy_toy_orm.internal

import groovy.sql.Sql
import org.apache.commons.lang3.StringUtils

import java.util.concurrent.TimeUnit

import static java.lang.System.currentTimeMillis

/**
 * Created with IntelliJ IDEA.
 * User: DavydovMD
 * Date: 27.02.13
 * Time: 18:07
 * To change this template use File | Settings | File Templates.
 */
@Singleton
class Util {

    /**
     * return first row/first column value from result set using Sql.firstRow.
     * @throws NoSuchElementException
     * @param db
     * @param query Sql query
     * @return
     */


    public static String lastToken(String string, String splitter = ".") {
        if (string.indexOf(splitter) == -1)
            return string

        StringUtils.split(string, ".").last();

    }

    public static String cutLastToken(String string, String splitter = ".") {
        if (string.indexOf(splitter) == -1)
            return string

        String[] arr = StringUtils.split(string, ".")
        StringBuilder b = new StringBuilder();
        for (int i=0;i<arr.length-1;i++){
            b.append(arr[i])
            b.append(splitter)
        }
        b.toString()
    }



    public static Object firstCell(Sql db, GString query) {
        def rowMap = db.firstRow(query)
        if (!rowMap) {
            throw new NoSuchElementException("Nothing was found for query:${query}")
        }
        rowMap.values().iterator()?.next()
    }

    public static Object firstCell(Sql db, String query) {
        def rowMap = db.firstRow(query)
        if (!rowMap) {
            throw new NoSuchElementException("Nothing was found for query:${query}")
        }
        rowMap.values().iterator()?.next()
    }

    public static int boolToInt(boolean b) {
        b ? 1 : 0
    }

    public static void sleepUntil(Date when) {
        Date now = new Date();
        if (now >= when) return
        Thread.sleep(when.getTime() - now.getTime())
    }

    private volatile static int counter = 42

    public static synchronized int nextId() {
        counter++
    }

    private static Random rnd = new Random(currentTimeMillis())

    public static Closure<Object> random = {
        Collection c = delegate as Collection
        if (c.size() == 0) return null

        int n = rnd.nextInt(c.size())
        if (c instanceof List) {
            return (c as List).get(n)
        } else {
            Iterator iter = c.iterator()
            Object res = null
            for (int i = 0; i < n; i++) {
                res = iter.next()
            }
            return res
        }

    } as Closure<Object>

//    public static Closure<Map> applyMapping = Map.metaClass.applyMapping;


    public static Date now() {
        new Date()
    }

    public static Date datePart(Date d) {
        Date date = new Date(d.getTime());                      // timestamp now

        Calendar cal = Calendar.getInstance();       // get calendar instance
        cal.setTime(date);                           // set cal to date
        cal.set(Calendar.HOUR_OF_DAY, 0);            // set hour to midnight
        cal.set(Calendar.MINUTE, 0);                 // set minute in hour
        cal.set(Calendar.SECOND, 0);                 // set second in minute
        cal.set(Calendar.MILLISECOND, 0);            // set millis in second
        return cal.getTime()             // actually computes the new Date
    }

    public static int toMillis(int interval, TimeUnit unit) {
        switch (unit) {
            case TimeUnit.NANOSECONDS:
                return interval / 1000000
            case TimeUnit.MICROSECONDS:
                return interval / 1000
            case TimeUnit.MILLISECONDS:
                return interval
            case TimeUnit.SECONDS:
                return interval * 1000
            case TimeUnit.MINUTES:
                return interval * 60000
            case TimeUnit.HOURS:
                return interval * 3600 * 1000
            case TimeUnit.DAYS:
                return interval * 24 * 3600 * 1000
        }
        throw new UnsupportedOperationException("?!!")
    }

    public static GString asGString(Object s) {
        GString.EMPTY.plus(s) as GString
    }


    static {
        Collection.metaClass.random {
            Closure<Object> randClosure = Util.random
            randClosure.delegate = delegate
            randClosure.call()
        }

        Map.metaClass.swapKeysWithValues {
            def ret = [:]

            delegate.each { Map.Entry entry ->
                ret.put(entry.getValue(), entry.getKey())
            }
            ret
        }


        Map.metaClass.applyMapping {Map mapping ->
            def ret = [:]
            delegate.each { Map.Entry entry ->
                ret.put(mapping[entry.getKey() ] ?: entry.getKey(), entry.getValue())
            }
            ret
        }

        /**
         *
         */
        Map.metaClass.enrich { Closure<Map> closure ->
            Map ret = new HashMap((Map) delegate)
            Map a = closure.call(delegate)
            ret.putAll(a)
            ret
        }

        Sql.metaClass.firstCell {
            String query ->
                firstCell((Sql) delegate, query)
        }

    }
}
